-----------------------------------------------------------------------------
-- Unified SMTP/FTP subsystem
-- LuaSocket toolkit.
-- Author: Diego Nehab
-- RCS ID: $Id: tp.lua,v 1.22 2006/03/14 09:04:15 diego Exp $
-----------------------------------------------------------------------------

-----------------------------------------------------------------------------
-- Declare module and import dependencies
-----------------------------------------------------------------------------
local base = _G
local string = require("string")
require("require/socket")
require("require/socket/ltn12")
local socket = socket
local ltn12 = ltn12
module("socket.tp")

-----------------------------------------------------------------------------
-- Program constants
-----------------------------------------------------------------------------
TIMEOUT = 60

-----------------------------------------------------------------------------
-- Implementation
-----------------------------------------------------------------------------
-- gets server reply (works for SMTP and FTP)
local function get_reply(c)
	local code, current, sep
	local line, err = c:receive()
	local reply = line
	if err then return nil, err end
	code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
	if not code then return nil, "invalid server reply" end
	if sep == "-" then -- reply is multiline
		repeat
			line, err = c:receive()
			if err then return nil, err end
			current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
			reply = reply .. "\n" .. line
		-- reply ends with same code
		until code == current and sep == " "
	end
	return code, reply
end

-- metatable for sock object
local metat = { __index = {} }

function metat.__index:check(ok)
	local code, reply = get_reply(self.c)
	if not code then return nil, reply end
	if base.type(ok) ~= "function" then
		if base.type(ok) == "table" then
			for i, v in base.ipairs(ok) do
				if string.find(code, v) then
					return base.tonumber(code), reply
				end
			end
			return nil, reply
		else
			if string.find(code, ok) then return base.tonumber(code), reply
			else return nil, reply end
		end
	else return ok(base.tonumber(code), reply) end
end

function metat.__index:command(cmd, arg)
	if arg then
		return self.c:send(cmd .. " " .. arg.. "\r\n")
	else
		return self.c:send(cmd .. "\r\n")
	end
end

function metat.__index:sink(snk, pat)
	local chunk, err = c:receive(pat)
	return snk(chunk, err)
end

function metat.__index:send(data)
	return self.c:send(data)
end

function metat.__index:receive(pat)
	return self.c:receive(pat)
end

function metat.__index:getfd()
	return self.c:getfd()
end

function metat.__index:dirty()
	return self.c:dirty()
end

function metat.__index:getcontrol()
	return self.c
end

function metat.__index:source(source, step)
	local sink = socket.sink("keep-open", self.c)
	local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step)
	return ret, err
end

-- closes the underlying c
function metat.__index:close()
	self.c:close()
	return 1
end

-- connect with server and return c object
function connect(host, port, timeout, create)
	base.print(host, port, timeout, create,socket.tcp)
	local c, e = (create or socket.tcp)()
	if not c then return nil, e end
	base.print("c",c)
	c:settimeout(timeout or TIMEOUT)
	local r, e = c:connect(host, port)
	base.print("r",r)
	base.print("e",e)
	if not r then
		c:close()
		return nil, e
	end
	return base.setmetatable({c = c}, metat)
end

